/* 
 * Copyright 2009 Shared Object (www.sharedobject.ch)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package edos.prm.activity;

import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

import edos.prm.artifact.PrmRuntimeException;
import edos.prm.engine.PRM;
import edos.prm.metrics.Metric;
import edos.prm.metrics.MetricsManager;



/**
 * An {@code Operation} is defined on a process {@code Activity}.
 * 
 * @author pawlak
 */
public interface Operation {
	
	/**
	 * Return the activity for which this operation is defined.
	 * 
	 * @return the activity to which this operation is bound
	 */
	public Activity getActivity();
	
	/**
	 * Execute the operation
	 * 
	 * @param args an array of arguments
	 * @return result of operation execution
	 */
	public Object execute(Object...args);
	
	/**
	 * Returns the Signature of the operation
	 * 
	 * @return the signature of this operation
	 */
	public Signature getSignature();
	
	/**
	 * Returns the operation's name
	 * 
	 * @return the string name of the operation
	 */
	public String getName();
	
	/**
	 * A {@code Signature} epresents the signatures of the arguments and
	 * return types in an operation.
	 */
	public final class Signature {
		private Class<?> returnType;
		private Class<?>[] paramTypes;
		public Signature(Class<?> returnType, Class<?>[] paramTypes) {
			this.returnType = returnType;
			this.paramTypes = paramTypes;
		}
		public Class<?>[] getParamTypes() {
			return paramTypes;
		}
		public Class<?> getReturnType() {
			return returnType;
		}
	}
	
	public final static class PrmOperation implements Operation {

		Method method;
		Activity activity;
		Signature sig;
		
		private static HashSet<PrmOperation> operations;
		
		static {
			operations = new HashSet<PrmOperation>();
		}
		
		public PrmOperation(Activity activity, String name, Signature sig) {
			this.activity = activity;
			try {
				this.method =
					activity.getClass().getDeclaredMethod(name, sig.paramTypes);
			} catch (Exception e) {
				e.printStackTrace();
			}
			this.sig = sig;
		}
		
		public Object execute(Object...args) {
			try {
				Object result = method.invoke(activity, args);
				// Metric
				MetricsManager mm = PRM.getMetricsManager();
				Set<Metric> suite = mm.getMetrics();
				for ( Metric metric : suite )
					if ( metric instanceof Metric.OperationalMetric ) {
						Metric.OperationalMetric opM = 
							(Metric.OperationalMetric)metric;
						if ( opM.getOperation().equals(this) )
							opM.getMetricExpression().evaluate(args);
					}
						
				return result;
			} catch (Exception e) {
				e.printStackTrace();
				throw new PrmRuntimeException("PrmOperation");
			}
		}

		public Activity getActivity() {
			return this.activity;
		}

		public String getName() {
			return method.getName();
		}

		public Signature getSignature() {
			return sig;
		}
		
		static void registerOperation(PrmOperation op) {
			operations.add(op);
		}
		
		public static Operation getOperation(Method method) {
			for ( PrmOperation operation : operations )
				if ( operation.method.equals(method) )
					return operation;
			throw new IllegalStateException("Cannot locate method " +
					method.getName());
		}
	}
}
